park#
用于创建锁和其他同步类的基本线程阻塞基元。
此类与使用它的每个线程相关联一个许可证(在 信号量 类的意义上)。如果许可证可用,呼叫 park 将立即返回,并在此过程中消耗它;否则 它可能会 阻止。如果许可证尚不可用,则致电提供 unpark 许可证。(不过,与信号量不同的是,许可证不会累积。最多有一个。
方法 park ,并提供 unpark 阻止和取消阻止线程的有效方法,这些线程不会遇到导致弃用方法 Thread. suspend 的问题并且 Thread. resume 无法用于此类目的:由于许可,一个线程调用 park 和另一个线程 unpark 尝试调用线程之间的争用将保持活动状态。此外, park 如果调用方的线程中断,则将返回,并且支持超时版本。该 park 方法也可能在任何其他时间返回,原因 “无缘无故”,因此通常必须在返回时重新检查条件的循环中调用。从这个意义上 park 说,它是对 “繁忙等待” 的优化,它不会浪费太多时间旋转,但必须与 unpark 配对才能有效。
每种形式的三种形式 park 也支持一个 blocker 对象参数。当线程被阻塞时,将记录此对象,以允许监视和诊断工具确定线程被阻塞的原因。(此类工具可以使用方法 getBlocker (Thread) 访问阻止程序。强烈建议使用这些表单,而不是没有此参数的原始表单。在锁实现中作为 blocker 提供的正常参数是 this。
并发控制的本质是协调多个线程,在需要的时候适当的协调线程的执行顺序,使得结果具有正确性。
第一,互斥,强调多个线程对同一临界区的访问控制,同一时间只能有一个线程进入临界区,不强调线程进入顺序。
值得注意的是互斥本身并不强调一个,而是有限个,比如一台电梯最多只能容纳 20 个人。
第二,同步,强调多个线程在并发执行时的先后顺序,例如:线程 A 在执行 a 前,一定要在线程 B 执行完 b 之后。
条件变量,是抽象的,并不绑定到某个资源,通过变量判断是否满足某个条件,从而觉得线程是否继续向后执行,就实现了这一层意义上的同步。从 Wiki 上对条件变量的定义来看,一个条件变量并不只是变量,也不只是条件。我们暂且忽略他以变量为结尾的,具有迷惑性的名称,我们暂且称其为 CV。首先 CV 包含一个对变量的断言,其次还包含一个等待该断言为真的线程队列。CV 具有如下行为,1. wait,调用线程等待断言为真。2. signal,通知某个等待队列中的线程,断言已经满足,可以继续执行。
信号量可以用来来模拟资源,同一时间几个资源。向上一层,锁就是一种资源,需要获取,底层是信号量。
管程,实际上就是一把锁 + 几个条件变量 (也就是上面自带等待队列的那种),是出于同步控制易用性而产生的一种封装。
依据此再来看 juc 中的 lock,semaphore,和 condition 就对应起来了,lock 和 condition 自由组合实现自定义管程。